package com.bigfans.catalogservice.api;

import com.bigfans.catalogservice.model.*;
import com.bigfans.catalogservice.service.product.ProductService;
import com.bigfans.catalogservice.service.sku.SkuService;
import com.bigfans.catalogservice.service.sku.StockLogService;
import com.bigfans.catalogservice.service.sku.StockService;
import com.bigfans.catalogservice.service.spec.SpecOptionService;
import com.bigfans.catalogservice.service.spec.SpecService;
import com.bigfans.catalogservice.service.spec.SpecValueService;
import com.bigfans.framework.Applications;
import com.bigfans.framework.CurrentUser;
import com.bigfans.framework.web.RestResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @author lichong
 * @create 2015-05-30 下午10:56
 **/
@RestController
public class SkuApi {

    @Autowired
    private ProductService productService;
    @Autowired
    private SkuService skuService;
    @Autowired
    private SpecOptionService specOptionService;
    @Autowired
    private SpecValueService specValueService;
    @Autowired
    private SpecService specService;
    @Autowired
    private StockService stockService;


    @GetMapping("/sku/{prodId}")
    public RestResponse getSku(@PathVariable(value = "prodId") String prodId) throws Exception {
        SkuResult result = new SkuResult();
        SKU selectedSku = skuService.getByPid(prodId);
        if (selectedSku == null) {
            return RestResponse.ok();
        }
        List<SKU> skuList = skuService.listByPgId(selectedSku.getPgId());
        if (skuList == null) {
            return RestResponse.ok();
        }
        Map<String , SKU> skuMap = skuList.stream().collect(Collectors.toMap(SKU::getSkuKey , Function.identity()));
        // 1. 保存所有的sku字符串
        Set<String> pgSkuVals = new HashSet<>();
        Map<String, Set<String>> optValsMap = new HashMap<>();
        for (SKU sku : skuList) {
            Map<String, String> optValMap = sku.getSkuMap();
            pgSkuVals.add(sku.getValKey());
            for (Map.Entry<String, String> entry : optValMap.entrySet()) {
                if (optValsMap.containsKey(entry.getKey())) {
                    optValsMap.get(entry.getKey()).add(entry.getValue());
                } else {
                    Set<String> vals = new HashSet<>();
                    vals.add(entry.getValue());
                    optValsMap.put(entry.getKey(), vals);
                }
            }
        }
        // 2. 根据当前选中的sku属性,确定出所有可选的sku组合
        Set<String> combinedValKeys = new HashSet<>();
        Map<String, String> selectedSkuMap = selectedSku.getSkuMap();
        for (Map.Entry<String, String> entry : selectedSkuMap.entrySet()) {

            // 取出当前pg中所有商品中的sku属性,和当前选中商品属性依次组合,产生可选的商品sku组合
            List<List<String>> skus = new ArrayList<>();
            for (Map.Entry<String, String> interEntry : selectedSkuMap.entrySet()) {
                if (entry.getKey().equals(interEntry.getKey())) {
                    skus.add(new ArrayList<>(optValsMap.get(interEntry.getKey())));
                    continue;
                }
                // 保证sku的顺序
                List<String> clonedKey = new ArrayList<>();
                clonedKey.add(interEntry.getValue());
                skus.add(clonedKey);
            }

            combinedValKeys.addAll(this.combine(skus));
        }

        // 3. 确定可以disable的valId
        List<String> valToDisable = new ArrayList<>();
        List<String> valToEnable = new ArrayList<>();
        List<String> outOfStock = new ArrayList<>();
        for (String valKey : combinedValKeys) {
            boolean disable = false;
            boolean oos = false;
            if (pgSkuVals.contains(valKey)) {
                Stock stock = stockService.getBySkuValKey(valKey);
                // 如果没有库存,添加到不可选列表
                if (stock == null || stock.getRest() <= 0) {
                    oos = true;
                }
            } else {
                disable = true;
            }
            String[] optValPairs = valKey.split(";");
            for (String optValPair : optValPairs) {
                if (disable) {
                    valToDisable.add(optValPair);
                } else if (oos) {
                    outOfStock.add(optValPair);
                } else {
                    valToEnable.add(optValPair);
                }
            }
        }
        // 4. 确定最终的不可选valId
        Set<String> disabledValIdList = new HashSet<>();
        Set<String> outOfStockList = new HashSet<>();
        // 4.1 不可点的valId
        for (String valD : valToDisable) {
            if (valToEnable.contains(valD)) {
                continue;
            }
            disabledValIdList.add(valD);
        }

        // 4.2 缺货的valId
        for (String valD : outOfStock) {
            if (valToEnable.contains(valD)) {
                continue;
            }
            outOfStockList.add(valD);
        }

        // 5. 创建返回结果
        List<SpecGroup> specGroups = new ArrayList<>();
        for (Map.Entry<String, Set<String>> entry : optValsMap.entrySet()) {
            String optId = entry.getKey();
            Set<String> valIds = entry.getValue();
            SpecOption specOption = specOptionService.load(optId);
            List<ProductSpec> productSpecs = new ArrayList<>();
            for (String valId : valIds) {
                List<String> selectedVals = selectedSku.getValIdList();

                boolean selectable = true;
                boolean selected = false;
                boolean oos = false;
                if (disabledValIdList.contains(valId)) {
                    selectable = false;
                }
                if(outOfStockList.contains(valId)){
                    oos = true;
                }
                if (selectedVals.contains(valId)) {
                    selected = true;
                }

                Map<String, String> currentSkuMap = new HashMap<>(selectedSku.getSkuMap());
                currentSkuMap.put(optId , valId);
                StringBuilder skuKey = new StringBuilder(32);
                for(Map.Entry<String , String> currentSkuEntry : currentSkuMap.entrySet()){
                    skuKey.append(currentSkuEntry.getKey());
                    skuKey.append(SKU.SKU_SEPARATOR);
                    skuKey.append(currentSkuEntry.getValue());
                    skuKey.append(SKU.KEYS_SEPARATOR);
                }
                skuKey.deleteCharAt(skuKey.length()-1);
                skuKey.trimToSize();
                SKU currentSku = skuMap.get(skuKey.toString());
                SpecValue specValue = specValueService.load(valId);
                ProductSpec productSpec = new ProductSpec();
                if(currentSku != null){
                    productSpec.setProdId(currentSku.getProdId());
                }
                productSpec.setOptionId(optId);
                productSpec.setOption(specOption.getName());
                productSpec.setValueId(specValue.getId());
                productSpec.setValue(specValue.getValue());
                productSpec.setOutOfStock(oos);
                productSpec.setSelectable(selectable);
                productSpec.setSelected(selected);
                productSpecs.add(productSpec);
            }
            SpecGroup group = new SpecGroup();
            group.setOption(specOption);
            group.setValues(productSpecs);
            specGroups.add(group);
        }

        result.setDisabledValIdList(disabledValIdList);
        result.setSelectedSku(selectedSku);
        result.setSkuMap(optValsMap);
        result.setSpecGroups(specGroups);

        return RestResponse.ok(result);
    }

    private List<String> combine(List<List<String>> lists) {
        List<String> heads = lists.get(0);
        for (int i = 1; i < lists.size(); i++) {
            List<String> result = new ArrayList<>();
            List<String> targets = lists.get(i);
            for (int j = 0; j < heads.size(); j++) {
                for (int k = 0; k < targets.size(); k++) {
                    result.add(heads.get(j) + ";" + targets.get(k));
                }
            }
            heads = result;
        }
        return heads;
    }
}
